/*
 * $Id: ClientDrvCommand.java,v 1.11 2009/08/20 19:57:07 navdeep_mahajan Exp $
 * 
 * Copyright (C) 2006 General Electric Company. All Rights Reserved.
 * 
 * This software is the confidential and proprietary information of the General
 * Electric Company (GE). You shall not disclose this software and shall use it
 * only in accordance with the terms of the license agreement you entered into
 * with GE.
 * 
 * GE MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
 * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
 * NON-INFRINGEMENT. GE SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
 * AS A RESULT OF USING, MODIFYING, OR DISTRIBUTING THIS SOFTWARE OR ITS
 * DERIVATIVES.
 */
package com.ge.healthcare.autosc.processor.iip.comm;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;

import com.ge.healthcare.autosc.common.ASCLogger;
import com.ge.healthcare.autosc.common.ASCUtil;
import com.ge.healthcare.autosc.common.commands.RExecCommand;
import com.ge.healthcare.autosc.common.exceptions.RExecCommandException;
import com.ge.healthcare.autosc.common.util.ASCIOHandler;
import com.ge.healthcare.autosc.common.util.ApplicationInfo;
import com.ge.healthcare.autosc.common.util.PropertyConstants;
import com.ge.healthcare.autosc.processor.exception.ClientDrvCommandException;

/**
 * @author 212042764
 * 
 */
public class ClientDrvCommand {

	private static final String NEW_LINE = "\n";
	private String username = null;
	private String passcode = null;
	private String clientdrvCmd = ApplicationInfo.getInstance().getProperty(
			PropertyConstants.CLIENT_DRV_COMMAND, "~insite/bin/clientdrv");
	private int waitForGreetings = ApplicationInfo.getInstance().getIntegerPropertyValue(
			PropertyConstants.CLIENT_DRV_GREETING_WAIT, "3");
	private RExecCommand rexecCommand = null;
	private DataInputStream inputStream = null;
	private DataOutputStream outputStream = null;

	/**
	 * Default Constructor
	 */
	public ClientDrvCommand() {
		super();
		rexecCommand = new RExecCommand();
	}
	
	/**
	 * Sets location of client drv command and greeting wait time 
	 */
	public ClientDrvCommand(String clientdrvCmd, int waitForGreetings) {
		super();
		rexecCommand = new RExecCommand();
		this.clientdrvCmd = clientdrvCmd;
		this.waitForGreetings = waitForGreetings;
	}
	
	/**
	 * AutoSC always connect through clientdrv so that rexec protocol can be
	 * kept active for the duration of activity
	 * 
	 * @param hostname
	 * @param username
	 * @param password
	 * @param timeoutMillis
	 * @throws ClientDrvCommandException
	 */
	public void connect(String hostname, String username, String password,
			int timeoutMillis) throws ClientDrvCommandException {
		String methodName = "connect";
		this.username = username;
		this.passcode = password;
		try {
			rexecCommand.connect(hostname, username, password, timeoutMillis);
			this.executeLoginCommand(clientdrvCmd);
			//Need to read input stream first as realAllCurrentInput will use the same
			this.inputStream = new DataInputStream(rexecCommand.getInputStream());
			ASCLogger.debug(this.getClass(), methodName, this
					.readAllCurrentInput(waitForGreetings));
			this.outputStream = new DataOutputStream(rexecCommand.getOutputStream());
		} catch (RExecCommandException rexecCmdException) {
			throw new ClientDrvCommandException("Failed to connect to host:"
					+ hostname, rexecCmdException);
		}
	}

	/**
	 * Connect to the client using a existing socket
	 * 
	 * @param socket
	 * @param timeout
	 * @throws ClientDrvCommandException
	 */
	public void connect(Socket socket, int timeout)
			throws ClientDrvCommandException {
		try {
			rexecCommand.connect(socket, timeout);
		} catch (IOException ioException) {
			throw new ClientDrvCommandException(
					"Failed to connect to provided socket:" + socket,
					ioException);
		}
	}
	
	/**
	 * Resets socket timeout to passed value
	 * 
	 * @param timeoutMillis
	 * @throws ClientDrvCommandException
	 */
	public void setTimeout(int timeoutMillis) throws ClientDrvCommandException {
		try {
			rexecCommand.setTimeout(timeoutMillis);
		} catch (RExecCommandException rexecException) {
			throw new ClientDrvCommandException(rexecException);
		}
	}

	/**
	 * Disconnects from clientdrv otherwise the session will remain open and
	 * timeout after some time
	 */
	public void disconnect() {
		rexecCommand.disconnect();
	}

	/**
	 * Specified the duration we are supposed to wait
	 * 
	 * @throws ClientDrvCommandException
	 */
	private void wait(int waitTime) throws ClientDrvCommandException {
		try {
			Thread.sleep(1000 * waitTime);
		} catch (InterruptedException interruptException) {
			throw new ClientDrvCommandException(
					"Failed to sleep on waiting for greetings",
					interruptException);
		}
	}

	/**
	 * Read all characters available on the input stream. This method reads byte
	 * by byte so that we won't close the socket, if we use readLine the socket
	 * will be killed
	 * 
	 * @throws ClientDrvCommandException
	 */
	public String readAllCurrentInput(int waitTime)
			throws ClientDrvCommandException {

		if (waitTime > 0) {
			this.wait(waitTime);
		}
		try {
			return ASCIOHandler.readAllCurrentInput(this.inputStream);
		} catch (IOException ioException) {
			throw new ClientDrvCommandException(
					"Failed to read from input stream", ioException);
		}
	}

	/**
	 * We will login now and run login command
	 * 
	 * @param command
	 *            - Command to run as part of login process, in our case its
	 *            clientdrv
	 * @throws ClientDrvCommandException
	 */
	private void executeLoginCommand(String command)
			throws ClientDrvCommandException {
		try {
			rexecCommand.executeCommand(command);
		} catch (RExecCommandException rexecCmdException) {
			throw new ClientDrvCommandException(new StringBuffer(
					"Failed to execute command:").append(command).append(
					" using username:").append(username).append(
					" and passcode:").append(passcode).toString(),
					rexecCmdException);
		}
	}

	/**
	 * Executes command against current login, appends a new line character at
	 * end of everything
	 * 
	 * @param command
	 * @throws ClientDrvCommandException
	 */
	public void executeCommand(String command) throws ClientDrvCommandException {
		String methodName = "executeCommand";
		ASCLogger.debug(this.getClass(), methodName, "Executing command " + command);
		try {
			if (ASCUtil.isNullOrEmptyObject(this.outputStream)) {
				ASCLogger.error(this.getClass(), methodName,
						"Outstream is null hence couldn't execute command:"
								+ command);
				return;
			}
			this.outputStream.writeBytes(command + NEW_LINE);
			this.outputStream.flush();
		} catch (IOException ioException) {
			throw new ClientDrvCommandException(new StringBuffer(
					"Failed to execute command:").append(command).append(
					" using username:").append(username).append(
					" and passcode:").append(passcode).toString(), ioException);
		}
	}
	
	/**
	 * Executes heartbeat command
	 * @param text
	 * @throws ClientDrvCommandException
	 */
	public void executeHearbeatCommand(String text) throws ClientDrvCommandException{
		this.executeCommand("display " + text);
	}
	
	/**
	 * Executes uniquefn command
	 * @throws ClientDrvCommandException
	 */
	public String executeUniquefn() throws ClientDrvCommandException{
		this.executeCommand("uniquefn");
		return this.getSingleLineResponse();
	}
	
	/**
	 * Executes sweep command on clientdrv
	 * @param sweepCmd
	 * @return
	 * @throws ClientDrvCommandException
	 */
	public String executeSweepCommand(String sweepCmd) throws ClientDrvCommandException{
		String cmd = new StringBuffer().append("sweep ").append(sweepCmd).toString();
		this.executeCommand(cmd);
		return this.getSingleLineResponse();
	}
	
	/**
	 * Logs out rexec with success message
	 * @throws ClientDrvCommandException
	 */
	public void completeTransWithSuccessMsg() throws ClientDrvCommandException{
		this.executeCommand("exit.succeed");
		this.disconnect();
	}
	
	/**
	 * Logs out rexec with failure message
	 * @throws ClientDrvCommandException
	 */
	public void completeTransWithFailureMsg() throws ClientDrvCommandException{
		this.executeCommand("exit.fail");
		this.disconnect();
	}
	
	/**
	 * Exceutes chmod command using client drv
	 * @param umod
	 * @param fileName
	 * @return
	 * @throws ClientDrvCommandException 
	 */
	public String executeChmodCommand(String umod, String fileName) throws ClientDrvCommandException{
		this.executeCommand(new StringBuffer().append("sweep chmod ").append(umod).append(
		" ").append(fileName).toString());
		return this.getSingleLineResponse();
	}

	/**
	 * Execute getinfo command using client drv
	 * @param argument
	 * @return
	 * @throws ClientDrvCommandException
	 */
	public String executeGetInfoCommand(String argument) throws ClientDrvCommandException {
		this.executeCommand("getinfo " + argument);
		return this.getSingleLineResponse();
	}
	
	/**
	 * Execute mkchf command using client drv
	 * @param argument
	 * @return	response from the target system
	 * @throws ClientDrvCommandException
	 */
	public String executeMkChfCommand() throws ClientDrvCommandException {
		this.executeCommand("mkchf");
		return this.getSingleLineResponse();
	}
	
	/**
	 * Execute trans.succeed commmand using client drv
	 * @param tranId	transaction Id
	 * @return	response from the target system
	 * @throws ClientDrvCommandException
	 */
	public String executeTransSucceedCommand(String tranId) throws ClientDrvCommandException {
		this.executeCommand("trans.succeed " + tranId);
		return this.getSingleLineResponse();
	}
	
	/**
	 * Execute trans.fail command using client drv
	 * @param tranId	transaction Id
	 * @return	response from the target system
	 * @throws ClientDrvCommandException
	 */
	public String executeTransFailCommand(String tranId) throws ClientDrvCommandException {
		this.executeCommand("trans.fail " + tranId);
		return this.getSingleLineResponse();
	}

	/**
	 * Execute exit.succeed command using client drv
	 * @return
	 * @throws ClientDrvCommandException
	 */
	public void executeExitSuccessCommand() throws ClientDrvCommandException {
		this.executeCommand("exit.succeed");
	}
	
	/**
	 * Execute exit.fail command using client drv
	 * @return
	 * @throws ClientDrvCommandException
	 */
	public void executeExitFailCommand() throws ClientDrvCommandException {
		this.executeCommand("exit.fail");
	}
	
	/**
	 * Execute standby command using client drv
	 * @param hrs	
	 * @return
	 * @throws ClientDrvCommandException
	 */
	public String executeStandbyCommand(int hrs) throws ClientDrvCommandException {
		this.executeCommand("standby " + Integer.valueOf(hrs).toString());
		return this.getSingleLineResponse();
	}
	
	/**
	 * This method reads single line of response, hence doesn't causes
	 * connection to break
	 * 
	 * @return
	 * @throws ClientDrvCommandException
	 */
	public String getSingleLineResponse() throws ClientDrvCommandException {
		try {
			String line = ASCIOHandler.readSingleLine(this.inputStream);
			ASCLogger.debug(this.getClass(), "getSingleLineResponse", "Read line: " + line);
			return line;
		} catch (IOException ioException) {
			throw new ClientDrvCommandException(ioException);
		}
	}

	/**
	 * Checks whether we are connected or not
	 * 
	 * @return
	 */
	public boolean isConnected() {
		
		if(rexecCommand.isConnected()) {
			return true;
		}
		/*
		 * If rexec is not connected, it may not be even used.
		 * Check if the inputStream or outputStream is defined. At the minimum to make sure they are defined.
		 */ 
		if((!ASCUtil.isNullOrEmptyObject(inputStream)) &&
		   (!ASCUtil.isNullOrEmptyObject(outputStream))) {
			return true;
		}
		return false;
	}

	/**
	 * @param inputStream the inputStream to set
	 */
	public void setInputStream(DataInputStream inputStream) {
		this.inputStream = inputStream;
	}

	/**
	 * @param outputStream the outputStream to set
	 */
	public void setOutputStream(DataOutputStream outputStream) {
		this.outputStream = outputStream;
	}
}
